package my.music.controller;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.util.IdUtil;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import my.music.common.enums.LoginQRCodeEnum;
import my.music.common.result.Result;
import my.music.common.result.ResultEnum;
import my.music.common.result.ResultModel;
import my.music.pojo.UserInfo;
import my.music.service.UserInfoService;
import my.music.dto.LoginDTO;
import my.music.dto.UserPwdDTO;
import my.music.util.PasswordUtil;
import my.music.vo.AuthQRVO;
import my.music.vo.AuthVO;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.math.BigInteger;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 认证
 */
@RestController
@RequestMapping("auth")
public class AuthController {
    @Resource
    private UserInfoService userService;
    @Resource
    private PasswordUtil passwordUtil;

    private final String LOGIN_QR_KEY = "login.qr.key:";
    private final String LOGIN_QR_OK = "login.qr.ok:";
    Cache<String, LoginQRCodeEnum> cache = Caffeine.newBuilder()
            .maximumSize(2000)
            .expireAfterWrite(1, TimeUnit.MINUTES)
            .build();

    Cache<String, AuthQRVO> cacheAuthQRVO = Caffeine.newBuilder()
            .maximumSize(2000)
            .expireAfterWrite(2, TimeUnit.MINUTES)
            .build();


    /**
     * 获取认证
     * （登录成功，token位于响应头中，请求时token格式为：”Authorization：'Bearer ' + token“）
     * @param loginDTO
     * @return
     */
    @PostMapping("login/account")
    public Result<AuthVO> loginByAccount(@RequestBody @Valid LoginDTO loginDTO, HttpServletResponse res){
        UserInfo dbUserInfo = userService.getUserInfoByAccount(loginDTO.getUserAccount());
        if (dbUserInfo == null){
            // 查询不到
            return ResultModel.error(ResultEnum.USER_NOTHINGNESS);
        }

        String encryption = passwordUtil.encryption(loginDTO.getUserPassword(), loginDTO.getUserAccount());
        if (encryption.equals(dbUserInfo.getUserPassword())){
            // 密码正确
            StpUtil.login(dbUserInfo.getId());
            List<String> permissionList = StpUtil.getPermissionList();
            AuthVO authVO = new AuthVO(dbUserInfo, permissionList);
            res.setHeader("Authorization", StpUtil.getTokenValue()); // 设置响应头
            return ResultModel.success(authVO);
        }

        return ResultModel.error(ResultEnum.USER_PASSWORD_ERROR);
    }

    /**
     * 二级认证
     * （成功后有120秒）
     * @param userPwdDTO
     * @return
     */
    @PostMapping("api/open/safe")
    public Result safe(@RequestBody @Valid UserPwdDTO userPwdDTO){
        // 获取id
        String loginId = StpUtil.getLoginIdAsString();
        UserInfo dbUserInfo = userService.getUserInfoById(new BigInteger(loginId));

        String encryption = passwordUtil.encryption(userPwdDTO.getUserPassword(), dbUserInfo.getUserAccount());
        if (encryption.equals(dbUserInfo.getUserPassword())){
            // 密码正确
            StpUtil.openSafe(120); // 在当前会话 开启二级认证，时间为120秒
            return ResultModel.success();
        }
        // 查询不到
        return ResultModel.error(ResultEnum.USER_PASSWORD_ERROR);
    }

    /**
     * 注销
     * @return
     */
    @GetMapping("api/logout")
    public Result logout(){
        StpUtil.logout();
        return ResultModel.success();
    }

    /**
     * 获取权限标识
     * @return
     */
    @GetMapping("api/list/permission")
    public Result<List<String>> listPermission(){
        List<String> permissionList = StpUtil.getPermissionList();
        return ResultModel.success(permissionList);
    }


    // 扫码登录 start

    /**
     * 获取登录操作token
     * @return
     */
    @GetMapping("get/qr/code/token")
    public Result getQRCodeToken(){
        String token = IdUtil.simpleUUID();
        cache.put(LOGIN_QR_KEY + token, LoginQRCodeEnum.UNUSED);
        return ResultModel.success(token);
    }

    /**
     * 获取扫码响应
     * @param token 登录操作token
     * @return
     */
    @GetMapping("get/qr/code/{token}")
    public Result getQRCodeResult(@PathVariable String token, HttpServletResponse res){
        AuthQRVO authQRVO = new AuthQRVO();
        LoginQRCodeEnum loginQRCodeEnum = cache.get(LOGIN_QR_KEY + token, k -> LoginQRCodeEnum.EXPIRE);
        authQRVO.setLoginQRCodeEnum(loginQRCodeEnum);
        if (LoginQRCodeEnum.CONFIRMED == loginQRCodeEnum){
            // 登录成功
            authQRVO = cacheAuthQRVO.getIfPresent(LOGIN_QR_OK + token); // 缓存登录成功信息
            res.setHeader("Authorization", authQRVO.getAuthorization()); // 设置响应头
        }
        return ResultModel.success(authQRVO);
    }

    /**
     * 确认登录
     * @param token
     * @param status CONFIRMED-确认登录，CONFIRMING-已扫码
     * @return
     */
    @GetMapping("api/update/qr/code/{token}/{status}")
    public synchronized Result updateQRCodeStatus(@PathVariable String token, @PathVariable String status){
        // 获取当前二维码状态
        LoginQRCodeEnum loginQRCodeEnum = cache.get(LOGIN_QR_KEY + token, k -> LoginQRCodeEnum.EXPIRE);

        String loginId = StpUtil.getLoginIdAsString();
        if ("CONFIRMED".equals(status)){
            // 确认登录
            System.err.println(LoginQRCodeEnum.CONFIRMING != loginQRCodeEnum);
            if (LoginQRCodeEnum.CONFIRMING != loginQRCodeEnum){ // 判断已扫码状态
                // 不是已扫码状态
                return ResultModel.error(ResultEnum.QR_CODE_EXPIRED);
            }

            cache.put(LOGIN_QR_KEY + token, LoginQRCodeEnum.CONFIRMED);

            UserInfo dbUserInfo = userService.getUserInfoById(new BigInteger(loginId));

            StpUtil.login(dbUserInfo.getId());

            AuthQRVO authQRVO = new AuthQRVO();
            authQRVO.setLoginQRCodeEnum(LoginQRCodeEnum.CONFIRMED);
            authQRVO.setUserInfo(dbUserInfo);
            authQRVO.setPermissionList(StpUtil.getPermissionList());
            authQRVO.setAuthorization(StpUtil.getTokenValue());
            cacheAuthQRVO.put(LOGIN_QR_OK + token, authQRVO); // 缓存登录成功信息
            return ResultModel.success(LoginQRCodeEnum.CONFIRMED);
        }else if ("CONFIRMING".equals(status)){
            // 已扫码
            cache.put(LOGIN_QR_KEY + token, LoginQRCodeEnum.CONFIRMING);
            return ResultModel.success(LoginQRCodeEnum.CONFIRMING);
        }
        // 拒绝登录
        cache.put(LOGIN_QR_KEY + token, LoginQRCodeEnum.CANCEL);
        return ResultModel.success(loginQRCodeEnum);
    }

    // 扫码登录 end
}
